{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 原文地址 https://blog.csdn.net/sakulafly/article/details/23128865\n",
    "\n",
    "# 目标\n",
    "\n",
    "视频的硬件解码近来发展非常快速，尤其是在低功耗的设备上。本教程会讲述一些硬件加速的背景知识并解释一下 GStreamer 是怎么做的。\n",
    "\n",
    "悄悄告诉你，如果设置正确地话，我们什么也不用做，GStreamer 自动做完这一切的。\n",
    "\n",
    "# 介绍\n",
    "\n",
    "视频解码是非常消耗 CPU 的一个任务，尤其是 1080P 这种高分辨率的高清节目。幸运的是，现在的显卡都带了可编程的 GPU，如果我们用 GPU 用来做视频解码，那么 CPU 就可以解放出来做其他的任务了。低功耗的 CPU 是无法做解码这样的工作的，这时硬件的配合就是必须的了。\n",
    "\n",
    "目前来说（June 2016)），每个 GPU 的制造商都提供了访问它们的硬件的方法（API），不幸的是各家并不相同，并没有一个强制的标准。\n",
    "\n",
    "截至 2016.06 已经有至少8种不同的视频解码加速API 。\n",
    "\n",
    "- **VAAPI**(Video Acceleration API)：2007 年 Intel 设计的，目的是在 Unix 操作系统的 XWindow 系统下运行，现在开源了。现在不仅仅局限于 Intel 的 GPU 了，其他制造商也可以使用了。GStreamer 通过 gstreamer-vaapi 这个插件来使用。\n",
    "\n",
    "- **VDPAU**(Video Decode and Presentation API for Unix)：2008 年 NVidia 设计的，最早也是运行在 Unix 的 XWindow 系统下，现在同样开源了。虽然同样已经是开源库了，但除了 NVidia 自己外还没有其他制造商使用。GStreamer 通过plugins-bad里的 `vdpau` 元素来使用。\n",
    "\n",
    "- DXVA(DirectX Video Acceleration): 微软为了 Windows 系统和 XBox360 定制的。GStreamer 通过 fluvadec 这个插件来使用。\n",
    "\n",
    "- OpenMAX(Open Media Acceleration)：由非盈利性联合 Khronos Group 设计的，是一组跨平台的 C 语言编程接口。GStreamer 通过 gstreamer-omx 这个插件来使用。\n",
    "\n",
    "- OVD(Open Video Decode)：AMD 的又一个 API，GStreamer 目前不能使用这个接口。\n",
    "\n",
    "- DCE(Distributed Codec Engine)：一个开源的软件库 (libdce) 和 TI 定制的 API，提供给 linux 系统和 ARM 平台的。GStreamer 通过 gstreamer-ducati 插件可以使用。\n",
    "\n",
    "- Android MediaCodec: 在可用的环境下，这是Android访问设备的硬件解码器和编码器的API。\n",
    "\n",
    "- Apple VideoTool Box Framework: 苹果的 API， GStreamer通过 applemedia 插件， 编码通过 `vtenc` 元素 ，解码通过 `vtdec` 元素.\n",
    "\n",
    "- Video4Linux: 最新的Linux 内核 提供了一种标准化的硬件编解码的 API。  GStreamer 通过 v4l2 插件在 `gst-plugins-good` 里. 编解码能力取决于平台。\n",
    "\n",
    "# 硬件加速视频解码插件内部的工作原理\n",
    "\n",
    "通常这些 API 提供了一系列的功能，比如：视频解码，后处理，解码帧的描述，或者把帧下载到系统内存等等。相应的，不同的功能插件一般是给不同的 element 使用的，这样 pipeline 可以适应任何需求。\n",
    "\n",
    "例如：gstreamer-vaapi 这个插件提供了 `vaapidecode`, `vaapipostproc` and `vaapisink` 这些 element，允许通过 VAAPI 来使用硬件加速功能，上传原始视频帧数据到 GPU 内存，下载 GPU 帧到系统内存并且描述 GPU 帧的内容。\n",
    "\n",
    "这里区分传统的 GStreamer 帧（在系统内存中）和由硬件加速 API 生成的帧是很重要的。硬件加速生成的帧位于 GPU 的内存中，是 GStreamer 不能直接操作的。通常他们是下载到系统内存中，然后就可以被当成普通帧来处理了，但留在 GPU 中由 GPU 来显示效率是最高的。\n",
    "\n",
    "GStreamer 需要追踪这些 “硬件缓冲区”，因为这样传统的缓冲区可以继续从一个 element 流向另一个 element，但他们的内容尽皆是硬件缓冲区的 ID 或者 Handler。比如：一个 appsink 获得了硬件缓冲区 ID，硬件缓冲区什么都不会响应，因为它们只能由生成它们的插件来处理。\n",
    "\n",
    "为了让这个更加明确，这些缓冲区都有特殊的 Caps，就像 video/x-vdpau-output 或者 video/x-fluendo-va 这样。在这种方式下，GStreamer 的自动插入机制不会试着把硬件缓冲区去传给传统的 element——因为他们根本风马牛不相及。而且，使用了这些 Caps 之后，自动插入机制就可以使用硬件加速来搭建 pipeline 了，因为，在 VAAIP 解码器之后，只有 VAAPI sink 这一种 element 是可以连接上去的。\n",
    "\n",
    "这些都说明，如果一个硬件加速的 API 在系统中可用而且对应的 GStreamer 插件也有的话，playbin 等自动连接的 element 可用随意的使用它们来搭建 pipeline，应用不需要做什么特殊的处理。\n",
    "\n",
    "当 playbin 必须在一些等效 element 中选择时，就像是选择传统的软件解码（例如;`vp8dec`)还是硬件加速（例如：vaapidecode）的解码，它会使用 `rank` 来决定。这个 rank 属性是每个 element 都有的，它会指明优先级，playbin 会选用能够构建完整的pipeline且最高的 rank 的 element 来搭建 pipeline。\n",
    "\n",
    "所以，playbin 是否使用硬件加速的 element 取决于当时所有可用的 element 的 rank 值。而且，最简单地确保硬件加速的 element 被选中的方法是修改 rank 属性的值。代码如下：\n",
    "\n",
    "```\n",
    "static void enable_factory (const gchar *name, gboolean enable) {\n",
    "    GstRegistry *registry = NULL;\n",
    "    GstElementFactory *factory = NULL;\n",
    "     \n",
    "    registry = gst_registry_get_default ();\n",
    "    if (!registry) return;\n",
    "     \n",
    "    factory = gst_element_factory_find (name);\n",
    "    if (!factory) return;\n",
    "     \n",
    "    if (enable) {\n",
    "        gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), GST_RANK_PRIMARY + 1);\n",
    "    }\n",
    "    else {\n",
    "        gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), GST_RANK_NONE);\n",
    "    }\n",
    "     \n",
    "    gst_registry_add_feature (registry, GST_PLUGIN_FEATURE (factory));\n",
    "    return;\n",
    "}\n",
    "```\n",
    "\n",
    "传给方法的第一个参数是要修改的 element 的名字，比如：`vaapidecode` 或者 `fluvadec`。\n",
    "\n",
    "这里主要的方法是 `gst_plugin_feature_set_rank()`，它会设置 element 的 rank。为了方便起见，rank 分成 NONE，MARGINAL，SECONDARY 和 PRIMARY，但任何数字都可以的。比如我们可用给某个 element 设置 PRIMARY+1，那么它就比其他设置成 PRIMARY 的 rank 高，设置一个 element 的 rank 是 NONE，会让自动连接机制屏蔽它（永远选不上）。\n",
    "\n",
    "> Warning: 当硬件解码器有缺陷时，GStreamer开发人员通常会将其排在软件解码器之后。这应该是一个警告\n",
    "\n",
    "---\n",
    "\n",
    "# 硬件加速视频解码和 GStreamer 的 SDK\n",
    "\n",
    "GStreamer 的 SDK 在 2012 年七月前是没有硬件加速的视频解码的插件的。主要原因是有些还没有完全写完，还有一些问题。但请记住这个情况会在不久改变。\n",
    "\n",
    "有些插件可用在它们公开源码的基础上自己编译出来，使用 Cerbero 编译系统。有些插件是供应商已经编译好了。\n",
    "\n",
    "下面会简单介绍一下当前这些插件的情况。\n",
    "\n",
    "- vdpad 在 gst-plugin-bad  \n",
    "针对 VDPAU 的 GStreamer element，在 gst-plugin-bad 里面，  \n",
    "支持 mpeg2，mpeg4 和 H264 的编解码\n",
    "\n",
    "- gstreamer-vaapi  \n",
    "针对 VAAPI 的 GStreamer element，项目的网址请猛戳[这里](https://gitorious.org/vaapi/gstreamer-vaapi)。  \n",
    "支持 mpeg2，mpeg4，H264，VC1 和 WMV3 的编解码  \n",
    "可用直接和 Clutter 配合使用，这样帧就可以一直在 GPU 里面  \n",
    "和 playbin2 兼容\n",
    "\n",
    "- gst-omx  \n",
    "针对 OpenMax 的 GStreamer element，项目网址请猛戳[这里](http://git.freedesktop.org/gstreamer/gst-omx/)。  \n",
    "在不同的硬件下支持不同的编解码\n",
    "\n",
    "- fluvadec  \n",
    "针对 VAAPI，VDPAU，DXVA2，XVBA 和 VDA 的 GStreamer element.  \n",
    "根据不同的 API，支持不同的编解码  \n",
    "可用直接和 Clutter 配合使用，这样帧就可以一直在 GPU 里面  \n",
    "和 playbin2 兼容\n",
    "\n",
    "|  | MPEG2 | MPEG4 | H.264 | VC1 |\n",
    "| --- | --- | --- | --- | --- |\n",
    "| VAAPI | ✓ | ✓ | ✓ | ✓ |\n",
    "| VDPAU | ✓ | ✓ | ✓ | ✓ |\n",
    "| XVBA |  |  | ✓ | ✓ |\n",
    "| DXVA2 |  |  | ✓ |  |\n",
    "| VDA |  |  | ✓ |  |\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
